/* 这是一个使用UDP通信协议的服务器端*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <ctype.h>
#include <sys/time.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SERV_PORT 8888

// udp服务器端无需进行accept和客户端进行连接
void udp_server(){
		int ret,  i, lfd;
		char buf[4096], clit_buf[INET_ADDRSTRLEN];
		struct sockaddr_in serv_addr, clit_addr;
		socklen_t clit_addr_len;

		// 1.先创建套接字:其中SOCK_STREAM为流式套接字（TCP专用）； SOCK_DERAM为报式套接字（UDP专用）
		lfd = socket(AF_INET, SOCK_DGRAM, 0);
		if(lfd == -1){
			perror("socket error!\n");
			exit(1);
		}
		// 2.使用bind函数进行绑定
		bzero(&serv_addr, sizeof(serv_addr));
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_port = htons(SERV_PORT);
		serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

		ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
		if(ret == -1){
			perror("bind error!\n");
			exit(1);
		}

		/*
		// 3.使用listen函数设置监听上限, 使用udp时无需设置监听上限
		ret = listen(lfd, 20);
		if(ret == -1){
			perror("listen  error!\n");
			exit(1);
		}*/

		printf("Accepting connecctions ...\n");

		// 4.因为UDP为无连接的，因此不必使用accept函数与客户端建立连接
		// 且read和recv只能用于TCP，因此需要换为recvfrom函数，write和send需要换位sendto函数
		clit_addr_len = sizeof(clit_addr);
		while(1){
			// 5. 直接从客户端读取数据即可
			ret = recvfrom(lfd, buf, sizeof(buf), 0, (struct sockaddr*)&clit_addr, &clit_addr_len);
			if(ret == -1){
				perror("recvfrom error!\n");
				exit(1);
			}else if(ret == 0){
				printf("client close()!\n");
				exit(1);
			}
			
			printf("the client [%s]:[%d] send data : %s \n", inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, clit_buf, sizeof(clit_buf)),
								ntohs(clit_addr.sin_port), buf);

			// 6. 进行操作，小写转大写，并写回客户端
			for(i=0; i<ret; ++i){
				buf[i] = toupper(buf[i]);
			}

			// 7. 使用sendto函数发送到UDP客户端
			ret = sendto(lfd, buf, ret, 0, (struct sockaddr*)&clit_addr, clit_addr_len);
			if(ret == -1){
				perror("sendto error!\n");
				exit(1);
			}
		}
		close(lfd);
	
}

int main(int argc, char *argv[]){
	udp_server();
}
